#include <stdio.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */
#include <math.h>
#include "sftpHdr.h"

#define BUF_SIZE 1024

int recvString(int sock, char *buf, unsigned int size) {
    unsigned char len;
    unsigned int recv_size = 0;
    
    if((recv_size = recv(sock, &len, sizeof(len), 0)) < 0) {
        return -1;
    }
    if (recv_size > size) {
        return recv_size;
    }
    if(recv(sock, buf, len, 0) < 0) {
        free(buf);
        return -1;
    }
    buf[len] = 0;
    
    return 0;
}

int sendString(int sock, const char *buf) {
    unsigned char len = strlen(buf);
    
    if(send(sock, &len, sizeof(len), 0) < 0) {
        return -1;
    }
    if(send(sock, buf, len, 0) < 0) {
        return -1;
    }
    
    return 0;
}

int recvLongString(int sock, char **out_buf, unsigned int *out_size) {
    unsigned int recv_size = 0, len;
    char *buf;
    
    if (recv(sock, &len, sizeof(len), 0) < 0) {
        return -1;
    }
    *out_size = len;
    buf = *out_buf = (char *)malloc(len + 1);
    memset(*out_buf, 0, len + 1);

    while (recv_size < len) {
        unsigned int s = 0;
        if ((s = recv(sock, &buf[recv_size], len - recv_size, 0)) < 0) {
            return -1;
        }
        recv_size += s;
    }

    return 0;
}

int sendLongString(int sock, const char *buf) {
    unsigned int len = strlen(buf);
    unsigned int send_size = 0;
    if (send(sock, &len, sizeof(len), 0) < 0) {
        return -1;
    }

    while(send_size < len) {
        unsigned int s = 0;
        if((s = send(sock, &buf[send_size], len - send_size, 0)) < 0) {
            return -1;
        }
        send_size += s;
    }

    return 0;
}

int recvFile(int clntSock, file_callback rfc) {
    char filename[256];
    FILE *fp;
    unsigned int tot_size = 0;
    
    if (recvString(clntSock, filename, 256) < 0) {
        printf("File is Too long\n");
        return -1;
    }
//    printf("FILE > filename : %s\n", filename);
    if ((fp = fopen(filename, "w")) != 0) {
        char buf[BUF_SIZE] = { 0, };
        int orig_size;
        
        recv(clntSock, &orig_size, sizeof(unsigned int), 0);
//        printf("original size : %d\n", orig_size);
        
        while (orig_size > tot_size) {
            int len;
            
            if((len = recv(clntSock, buf, BUF_SIZE, 0)) < 0) {
                fclose(fp);
                return -1;
            }
            fwrite(buf, sizeof(char), len, fp);
            memset(buf, 0, sizeof(buf));
            
            tot_size += len;
            
//            printf("File Upload %d%% Finish\n", (tot_size/orig_size)*100);
//            printf("recv_size : %d\n", tot_size);
            if (rfc)
                rfc((double)tot_size/(double)orig_size);
        }
        
        fclose(fp);
    }
    
    if (sendString(clntSock, FileAck) < 0) {
        return -1;
    }
    
    return 0;
}

int sendFile(int sock, const char *filename, file_callback fc) {
    struct stat info;
    char buf[BUF_SIZE];
    FILE *fp;
    unsigned int send_size = 0, tot_size;
    
    memset(&info, 0, sizeof(info));
    stat(filename, &info);
    
//    printf("File put/get > filename : %s\n", filename);
    
    sendString(sock, filename);
    
    send(sock, &info.st_size, sizeof(unsigned int), 0);
    tot_size = info.st_size;
    
    if((fp = fopen(filename, "r")) != 0) {
        unsigned int len = 0;
        
        while(!feof(fp)) {
            memset(buf, 0, sizeof(buf));
            len = fread(buf, sizeof(char), BUF_SIZE, fp);
            send(sock, buf, len, 0);
            send_size += len;
            
            if (fc != 0) 
                fc((double)send_size/(double)tot_size);
        }
        
        fclose(fp);
    }
    
    recvString(sock, buf, BUF_SIZE);
    if(!strcmp(buf, FileAck)) {
        return 0;
    }
    
    return -1;
}
